#include "typedef.h"
#include "asm/includes.h"
#include "asm/gpio.h"
#include "asm/power/p33.h"


#define PR_IN(gpio)          (!!(BIT(gpio - IO_PORT_R0) & P33_CON_GET(R3_PR_IN)))
/* #define PR_PU(gpio, val)     P33_CON_SET(R3_PR_PU, gpio - IO_PORT_R0, 1, val) */
/* #define PR_PD(gpio, val)     P33_CON_SET(R3_PR_PD, gpio - IO_PORT_R0, 1, val) */
/* #define PR_HD(gpio, val)     P33_CON_SET(R3_PR_HD, gpio - IO_PORT_R0, 1, val) */
/* #define PR_OUT(gpio, val)    P33_CON_SET(R3_PR_OUT, gpio - IO_PORT_R0, 1, val) */
/* #define PR_DIR(gpio, val)    P33_CON_SET(R3_PR_DIE, gpio - IO_PORT_R0, 1, val) */
/* #define PR_DIE(gpio, val)    P33_CON_SET(R3_PR_DIE, gpio - IO_PORT_R0, 1, val) */
#define PR_SET(gpio, reg_name, value)	P33_CON_SET(R3_PR_##reg_name, gpio - IO_PORT_R0, 1, value)


#define USB_DP_OUT(x)   JL_USB_IO->CON0 = ((JL_USB_IO->CON0 & ~BIT(0)) | ((x & 0x1)<<0))
#define USB_DM_OUT(x)   JL_USB_IO->CON0 = ((JL_USB_IO->CON0 & ~BIT(1)) | ((x & 0x1)<<1))
#define USB_DP_DIR(x)   JL_USB_IO->CON0 = ((JL_USB_IO->CON0 & ~BIT(2)) | ((x & 0x1)<<2))
#define USB_DM_DIR(x)   JL_USB_IO->CON0 = ((JL_USB_IO->CON0 & ~BIT(3)) | ((x & 0x1)<<3))
#define USB_DP_PD(x)    JL_USB_IO->CON0 = ((JL_USB_IO->CON0 & ~BIT(4)) | ((x & 0x1)<<4))
#define USB_DM_PD(x)    JL_USB_IO->CON0 = ((JL_USB_IO->CON0 & ~BIT(5)) | ((x & 0x1)<<5))
#define USB_DP_PU(x)    JL_USB_IO->CON0 = ((JL_USB_IO->CON0 & ~BIT(6)) | ((x & 0x1)<<6))
#define USB_DM_PU(x)    JL_USB_IO->CON0 = ((JL_USB_IO->CON0 & ~BIT(7)) | ((x & 0x1)<<7))
#define USB_DP_DIE(x)   JL_USB_IO->CON0 = ((JL_USB_IO->CON0 & ~BIT(9)) | ((x & 0x1)<<9))
#define USB_DM_DIE(x)   JL_USB_IO->CON0 = ((JL_USB_IO->CON0 & ~BIT(10)) | ((x & 0x1)<<10))
#define USB_DP_DIEH(x)  JL_USB_IO->CON0 = ((JL_USB_IO->CON0 & ~BIT(13)) | ((x & 0x1)<<13))
#define USB_DM_DIEH(x)  JL_USB_IO->CON0 = ((JL_USB_IO->CON0 & ~BIT(14)) | ((x & 0x1)<<14))

#define USB_DP_IN()     (!!(JL_USB->CON0 & BIT(17)))
#define USB_DM_IN()     (!!(JL_USB->CON0 & BIT(16)))

#define USB_SET(gpio, reg_name, value)	\
	do { \
		if (IO_PORT_DP == (gpio)) { \
			USB_DP_##reg_name(value); \
		} \
		if (IO_PORT_DM == (gpio)) { \
			USB_DM_##reg_name(value); \
		} \
	} while (0)


static const u32 gpio_regs[] = {
    (u32) JL_PORTA,
    (u32) JL_PORTB,
    (u32) JL_PORTC,
    (u32) JL_PORTD,
};


#define __gpio_mask(gpio)   BIT((gpio) % IO_GROUP_NUM)

struct gpio_reg *gpio2reg(u32 gpio)
{
    if (gpio > IO_MAX_NUM) {
        return NULL;
    }
    return (struct gpio_reg *)gpio_regs[gpio / IO_GROUP_NUM];
}

__attribute__((always_inline_when_const_args))
u32 usb_set_direction(u32 gpio, u32 value)
{
    gpio -= IO_PORT_DP;
    if (value) {
        JL_USB_IO->CON0 |= BIT(gpio + 2);
    } else {
        JL_USB_IO->CON0 &= ~BIT(gpio + 2);
    }
    return 0;
}

__attribute__((always_inline_when_const_args))
u32 usb_output(u32 gpio, u32 value)
{
    gpio -= IO_PORT_DP;
    if (value) {
        JL_USB_IO->CON0 |= BIT(gpio);
    } else {
        JL_USB_IO->CON0 &= ~BIT(gpio);
    }
    return 0;
}

__attribute__((always_inline_when_const_args))
u32 usb_set_pull_up(u32 gpio, u32 value)
{
    gpio -= IO_PORT_DP;
    if (value) {
        JL_USB_IO->CON0 |= BIT(gpio + 6);
    } else {
        JL_USB_IO->CON0 &= ~BIT(gpio + 6);
    }
    return 0;
}

__attribute__((always_inline_when_const_args))
u32 usb_set_pull_down(u32 gpio, u32 value)
{
    gpio -= IO_PORT_DP;
    if (value) {
        JL_USB_IO->CON0 |= BIT(gpio + 4);
    } else {
        JL_USB_IO->CON0 &= ~BIT(gpio + 4);
    }
    return 0;
}

__attribute__((always_inline_when_const_args))
u32 usb_set_die(u32 gpio, u32 value)
{
    gpio -= IO_PORT_DP;
    if (value) {
        JL_USB_IO->CON0 |= BIT(gpio + 9);
    } else {
        JL_USB_IO->CON0 &= ~BIT(gpio + 9);
    }
    return 0;
}

__attribute__((always_inline_when_const_args))
u32 usb_set_dieh(u32 gpio, u32 value)
{
    gpio -= IO_PORT_DP;
    if (value) {
        JL_USB_IO->CON0 |= BIT(gpio + 13);
    } else {
        JL_USB_IO->CON0 &= ~BIT(gpio + 13);
    }
    return 0;
}

__attribute__((always_inline_when_const_args))
u32 usb_read(u32 gpio)
{
    gpio -= IO_PORT_DP;
    u32 v = JL_USB->CON0 & (BIT(17 - gpio));
    return !!v;
}

__attribute__((always_inline_when_const_args))
void usb_iomode(u32 enable)
{
    if (enable) {
        JL_USB->CON0 &= ~BIT(0);//USB_PHY_OFF  = 0;
        JL_USB_IO->CON0 |= BIT(8) | BIT(11);
    } else {
        JL_USB_IO->CON0 &= ~(BIT(8) | BIT(11));
    }
}

__attribute__((always_inline_when_const_args))
int gpio_set_direction(u32 gpio, u32 value)
{
    u32 mask;
    struct gpio_reg *g;
    if ((gpio == IO_PORT_DP) || (gpio == IO_PORT_DM)) {
        usb_iomode(1);
        USB_SET(gpio, DIR, value);
        return 0;
    } else if ((gpio == IO_PORT_R0) || (gpio == IO_PORT_R1)) {
        p33_tx_1byte(R3_OSL_CON, 0);
        PR_SET(gpio, DIR, value);
    }
    g = gpio2reg(gpio);
    if (!g) {
        return -EINVAL;
    }
    mask = __gpio_mask(gpio);
    if (value) {
        g->dir |= mask;
    } else {
        g->dir &= ~mask;
    }
    return 0;
}

__attribute__((always_inline_when_const_args))
int gpio_set_output_value(u32 gpio, u32 value)
{
    u32 mask;
    struct gpio_reg *g;
    if ((gpio == IO_PORT_DP) || (gpio == IO_PORT_DM)) {
        USB_SET(gpio, OUT, value);
        return 0;
    } else if ((gpio == IO_PORT_R0) || (gpio == IO_PORT_R1)) {
        PR_SET(gpio, OUT, value);
    }
    g = gpio2reg(gpio);
    if (!g) {
        return -EINVAL;
    }
    mask = __gpio_mask(gpio);
    if (value == 1) {
        g->out |= mask;
    } else {
        g->out &= ~mask;
    }
    return 0;
}

__attribute__((always_inline_when_const_args))
int gpio_read_input_value(u32 gpio)
{
    u32 mask;
    struct gpio_reg *g;
    if (gpio == IO_PORT_DP) {
        return USB_DP_IN();
    } else if (gpio == IO_PORT_DM) {
        return USB_DM_IN();
    } else if ((gpio == IO_PORT_R0) || (gpio == IO_PORT_R1)) {
        return PR_IN(gpio);
    }
    g = gpio2reg(gpio);
    if (!g) {
        return -EINVAL;
    }
    mask = __gpio_mask(gpio);
    int v = !!(g->in & mask);
    return v;
}

__attribute__((always_inline_when_const_args))
int gpio_direction_input(u32 gpio)
{
    u32 mask;
    struct gpio_reg *g;
    if ((gpio == IO_PORT_DP) || (gpio == IO_PORT_DM)) {
        usb_iomode(1);
        USB_SET(gpio, DIR, 1);
        return 0;
    } else if ((gpio == IO_PORT_R0) || (gpio == IO_PORT_R1)) {
        p33_tx_1byte(R3_OSL_CON, 0);
        PR_SET(gpio, DIR, 1);
        return 0;
    }
    g = gpio2reg(gpio);
    if (!g) {
        return -EINVAL;
    }
    mask = __gpio_mask(gpio);
    g->dir |= mask;
    return 0;
}

__attribute__((always_inline_when_const_args))
int gpio_dir(u32 gpio, u32 start, u32 len, u32 dat, enum gpio_op_mode op)
{
    struct gpio_reg *g;
    start = start > gpio ? start - gpio : start;
    dat = dat & (BIT(len) - 1);
    g = gpio2reg(gpio);
    if (!g) {
        return -EINVAL;
    }
    u32 sfr = g->dir;
    switch (op) {
    case GPIO_SET:
        SFR(sfr, start, len, dat);
        break;
    case GPIO_AND:
        sfr &= (dat << start);
        break;
    case GPIO_OR:
        sfr |= (dat << start);
        break;
    case GPIO_XOR:
        sfr ^= (dat << start);
        break;
    }
    g->dir = sfr;
    return 0;
}

__attribute__((always_inline_when_const_args))
int gpio_direction_output(u32 gpio, u32 value)
{
    u32 mask;
    struct gpio_reg *g;

    if ((gpio == IO_PORT_DP) || (gpio == IO_PORT_DM)) {
        usb_iomode(1);
        USB_SET(gpio, OUT, value);
        USB_SET(gpio, DIR, 0);
        return 0;
    } else if ((gpio == IO_PORT_R0) || (gpio == IO_PORT_R1)) {
        p33_tx_1byte(R3_OSL_CON, 0);
        PR_SET(gpio, OUT, value);
        PR_SET(gpio, DIR, 0);
        return 0;
    }
    g = gpio2reg(gpio);
    if (!g) {
        return -EINVAL;
    }
    mask = __gpio_mask(gpio);
    if (value == 1) {
        g->out |= mask;
    } else if (value == 0) {
        g->out &= ~mask;
    }
    g->dir &= ~mask;
    return 0;
}

__attribute__((always_inline_when_const_args))
int gpio_write(u32 gpio, u32 value)
{
    u32 mask;
    struct gpio_reg *g;
    if ((gpio == IO_PORT_DP) || (gpio == IO_PORT_DM)) {
        USB_SET(gpio, OUT, value);
        return 0;
    } else if ((gpio == IO_PORT_R0) || (gpio == IO_PORT_R1)) {
        PR_SET(gpio, OUT, value);
        return 0;
    }
    g = gpio2reg(gpio);
    if (!g) {
        return -EINVAL;
    }
    mask = __gpio_mask(gpio);
    if (value == 1) {
        g->out |= mask;
    } else {
        g->out &= ~mask;
    }
    return 0;
}
__attribute__((always_inline_when_const_args))
int gpio_out(u32 gpio, u32 start, u32 len, u32 dat)
{
    struct gpio_reg *g;
    start = start > gpio ? start - gpio : start;
    g = gpio2reg(gpio);
    if (!g) {
        return -EINVAL;
    }
    u32 sfr = g->out;
    SFR(sfr, start, len, dat);
    g->out = sfr;

    return 0;
}
__attribute__((always_inline_when_const_args))
int gpio_read(u32 gpio)
{
    u32 mask;
    struct gpio_reg *g;
    if (gpio == IO_PORT_DP) {
        return USB_DP_IN();
    } else if (gpio == IO_PORT_DM) {
        return USB_DM_IN();
    } else if ((gpio == IO_PORT_R0) || (gpio == IO_PORT_R1)) {
        return PR_IN(gpio);
    }
    g = gpio2reg(gpio);
    if (!g) {
        return -EINVAL;
    }
    mask = __gpio_mask(gpio);
    int v = !!(g->in & mask);
    return v;
}
__attribute__((always_inline_when_const_args))
int gpio_in(u32 gpio)
{
    u32 mask;
    struct gpio_reg *g;
    g = gpio2reg(gpio);
    if (!g) {
        return -EINVAL;
    }
    mask = __gpio_mask(gpio);
    u32 v = g->in;
    return v;
}
__attribute__((always_inline_when_const_args))
int gpio_set_pull_up(u32 gpio, u32 value)
{
    u32 mask;
    struct gpio_reg *g;
    if ((gpio == IO_PORT_DP) || (gpio == IO_PORT_DM)) {
        USB_SET(gpio, PU, value);
        return 0;
    } else if ((gpio == IO_PORT_R0) || (gpio == IO_PORT_R1)) {
        PR_SET(gpio, PU, value);
        return 0;
    }
    g = gpio2reg(gpio);
    if (!g) {
        return -EINVAL;
    }
    mask = __gpio_mask(gpio);
    if (value) {
        g->pu |= mask;
    } else {
        g->pu &= ~mask;
    }
    return 0;
}
__attribute__((always_inline_when_const_args))
int gpio_set_pu(u32 gpio, u32 start, u32 len, u32 dat, enum gpio_op_mode op)
{
    struct gpio_reg *g;
    start = start > gpio ? start - gpio : start;
    dat = dat & (BIT(len) - 1);
    g = gpio2reg(gpio);
    if (!g) {
        return -EINVAL;
    }
    u32 sfr = g->pu;
    switch (op) {
    case GPIO_SET:
        SFR(sfr, start, len, dat);
        break;
    case GPIO_AND:
        sfr &= (dat << start);
        break;
    case GPIO_OR:
        sfr |= (dat << start);
        break;
    case GPIO_XOR:
        sfr ^= (dat << start);
        break;
    }
    g->pu = sfr;
    return 0;
}
__attribute__((always_inline_when_const_args))
int gpio_set_pull_down(u32 gpio, u32 value)
{
    u32 mask;
    struct gpio_reg *g;
    if ((gpio == IO_PORT_DP) || (gpio == IO_PORT_DM)) {
        USB_SET(gpio, PD, value);
        return 0;
    } else if ((gpio == IO_PORT_R0) || (gpio == IO_PORT_R1)) {
        PR_SET(gpio, PD, value);
        return 0;
    }
    g = gpio2reg(gpio);
    if (!g) {
        return -EINVAL;
    }
    mask = __gpio_mask(gpio);
    if (value) {
        g->pd |= mask;
    } else {
        g->pd &= ~mask;
    }
    return 0;
}
__attribute__((always_inline_when_const_args))
int gpio_set_pd(u32 gpio, u32 start, u32 len, u32 dat, enum gpio_op_mode op)
{
    struct gpio_reg *g;
    start = start > gpio ? start - gpio : start;
    dat = dat & (BIT(len) - 1);
    g = gpio2reg(gpio);
    if (!g) {
        return -EINVAL;
    }
    u32 sfr = g->pd;
    switch (op) {
    case GPIO_SET:
        SFR(sfr, start, len, dat);
        break;
    case GPIO_AND:
        sfr &= (dat << start);
        break;
    case GPIO_OR:
        sfr |= (dat << start);
        break;
    case GPIO_XOR:
        sfr ^= (dat << start);
        break;
    }
    g->pd = sfr;
    return 0;
}
__attribute__((always_inline_when_const_args))
int gpio_set_hd0(u32 gpio, u32 value)
{
    u32 mask;
    struct gpio_reg *g;
    g = gpio2reg(gpio);
    if (!g) {
        return -EINVAL;
    }
    mask = __gpio_mask(gpio);
    if (value) {
        g->hd0 |= mask;
    } else {
        g->hd0 &= ~mask;
    }
    return 0;
}
__attribute__((always_inline_when_const_args))
int gpio_set_hd(u32 gpio, u32 value)
{
    u32 mask;
    struct gpio_reg *g;

    if ((gpio == IO_PORT_R0) || (gpio == IO_PORT_R1)) {
        PR_SET(gpio, HD, value);
        return 0;
    }
    g = gpio2reg(gpio);
    if (!g) {
        return -EINVAL;
    }
    mask = __gpio_mask(gpio);
    if (value) {
        g->hd |= mask;
    } else {
        g->hd &= ~mask;
    }
    return 0;
}
__attribute__((always_inline_when_const_args))
int gpio_set_die(u32 gpio, u32 value)
{
    u32 mask;
    struct gpio_reg *g;
    if ((gpio == IO_PORT_DP) || (gpio == IO_PORT_DM)) {
        USB_SET(gpio, DIE, value);
        return 0;
    } else if ((gpio == IO_PORT_R0) || (gpio == IO_PORT_R1)) {
        PR_SET(gpio, DIE, value);
        return 0;
    }
    g = gpio2reg(gpio);
    if (!g) {
        return -EINVAL;
    }
    mask = __gpio_mask(gpio);
    if (value) {
        g->die |= mask;
    } else {
        g->die &= ~mask;
    }
    return 0;
}
__attribute__((always_inline_when_const_args))
int gpio_set_dieh(u32 gpio, u32 value)
{
    u32 mask;
    struct gpio_reg *g;

    if ((gpio == IO_PORT_DP) || (gpio == IO_PORT_DM)) {
        USB_SET(gpio, DIEH, value);
        return 0;
    }
    g = gpio2reg(gpio);
    if (!g) {
        return -EINVAL;
    }
    mask = __gpio_mask(gpio);
    if (value) {
        g->dieh |= mask;
    } else {
        g->dieh &= ~mask;
    }
    return 0;
}
__attribute__((always_inline_when_const_args))
int gpio_die(u32 gpio, u32 start, u32 len, u32 dat, enum gpio_op_mode op)
{
    struct gpio_reg *g;
    start = start > gpio ? start - gpio : start;
    dat = dat & (BIT(len) - 1);
    g = gpio2reg(gpio);
    if (!g) {
        return -EINVAL;
    }
    u32 sfr = g->die;
    switch (op) {
    case GPIO_SET:
        SFR(sfr, start, len, dat);
        break;
    case GPIO_AND:
        sfr &= (dat << start);
        break;
    case GPIO_OR:
        sfr |= (dat << start);
        break;
    case GPIO_XOR:
        sfr ^= (dat << start);
        break;
    }
    g->die = sfr;
    return 0;
}
__attribute__((always_inline_when_const_args))
int gpio_dieh(u32 gpio, u32 start, u32 len, u32 dat, enum gpio_op_mode op)
{
    struct gpio_reg *g;
    start = start > gpio ? start - gpio : start;
    dat = dat & (BIT(len) - 1);
    g = gpio2reg(gpio);
    if (!g) {
        return -EINVAL;
    }
    u32 sfr = g->dieh;
    switch (op) {
    case GPIO_SET:
        SFR(sfr, start, len, dat);
        break;
    case GPIO_AND:
        sfr &= (dat << start);
        break;
    case GPIO_OR:
        sfr |= (dat << start);
        break;
    case GPIO_XOR:
        sfr ^= (dat << start);
        break;
    }
    g->dieh = sfr;
    return 0;
}

__attribute__((always_inline_when_const_args))
u32 get_gpio(const char *p)
{
    u32 port;
    if (p[0] == 'P') {
        if (p[1] >= 'A' && p[1] <= 'H') {
            port = p[1] - 'A';
            port *= IO_GROUP_NUM;
        } else if (p[1] == 'R') {
            return (IO_PORT_R0 + (p[2] - '0'));
        } else {
            return -EINVAL;
        }
        int mask = (p[2] - '0') * 10 + (p[3] - '0');
        if (mask < 16) {
            port += mask;
            return port;
        }
    }
    if (strcmp(p, "USBDP") == 0) {
        return  IO_PORT_DP;
    }
    if (strcmp(p, "USBDM") == 0) {
        return  IO_PORT_DM;
    }
    return -EINVAL;
}

__attribute__((always_inline_when_const_args))
int gpio_output_channel(u32 gpio, u32 CHx_xxx)
{
    u8 CHx = CHx_xxx / 0x10;
    u8 xxx = CHx_xxx % 0x10;
    switch (CHx) {
    case 0 :
        JL_IOMAP->CON1 &= ~(0xf << 8);
        JL_IOMAP->CON1 |= (xxx << 8);
        break;
    case 1:
    case 2:
        JL_IOMAP->CON3 &= ~(0xf << (20 + (CHx - 1) * 4));
        JL_IOMAP->CON3 |= (xxx << (20 + (CHx - 1) * 4));
        break;
    default:
        return -EINVAL;
    }

    gpio_set_direction(gpio, 0);
    gpio_set_pull_up(gpio, 1);
    gpio_set_pull_down(gpio, 1);
    switch (CHx) {
    case 0:
        gpio_write(gpio, 0);
        gpio_set_die(gpio, 0);
        break;
    case 1:
        gpio_write(gpio, 0);
        gpio_set_die(gpio, 1);
        break;
    case 2:
        gpio_write(gpio, 1);
        gpio_set_die(gpio, 0);
        break;
    case 3:
        gpio_write(gpio, 1);
        gpio_set_die(gpio, 1);
        break;
    default:
        return -EINVAL;
    }
    return 0;
}

__attribute__((always_inline_when_const_args))
int gpio_uart_rx_input_channel(u32 gpio, u32 uart_idx, u32 ch)
{
    u32 mask;
    struct gpio_reg *g;

    if (gpio > IO_PORT_DM) {
        return -EINVAL;
    }
    if (gpio >= IO_MAX_NUM && gpio < IO_PORT_DP) {
        return -EINVAL;
    }

    if (gpio == IO_PORT_DP) {
        usb_iomode(1);
        gpio = 61;
    } else if (gpio == IO_PORT_DM) {
        usb_iomode(1);
        gpio = 62;
    }
    if (gpio < IO_MAX_NUM) {
        g = gpio2reg(gpio);
        if (!g) {
            return -EINVAL;
        }
    }

    switch (uart_idx) {
    case 0:
        JL_IOMAP->CON3 &= ~(0b111 << 0);
        JL_IOMAP->CON3 |= ((0b100 + ch) << 0);
        break;
    case 1:
        JL_IOMAP->CON3 &= ~(0b111 << 4);
        JL_IOMAP->CON3 |= ((0b100 + ch) << 4);
        break;
    case 2:
        JL_IOMAP->CON3 &= ~(0b111 << 8);
        JL_IOMAP->CON3 |= ((0b100 + ch) << 8);
        break;
    default:
        return -EINVAL;
    }

    switch (ch) {
    case 0 :
        JL_IOMAP->CON2 &= ~(0b111111 << 0);
        JL_IOMAP->CON2 |= gpio << 0;
        break;
    case 1 :
        JL_IOMAP->CON2 &= ~(0b111111 << 8);
        JL_IOMAP->CON2 |= gpio << 8;
        break;
    case 2 :
        JL_IOMAP->CON2 &= ~(0b111111 << 16);
        JL_IOMAP->CON2 |= gpio << 16;
        break;
    case 3:
        JL_IOMAP->CON2 &= ~(0b111111 << 24);
        JL_IOMAP->CON2 |= gpio << 24;
        break;
    default :
        return -EINVAL;
    }

    if (gpio >= IO_PORT_DP) {
        gpio -= 5;
        usb_iomode(1);
        usb_set_die(gpio, 1);
        usb_set_direction(gpio, 1); //作为输入模式
        usb_set_pull_up(gpio, 0); //不下拉
        usb_set_pull_down(gpio, 0); //不上拉
    } else {
        mask = __gpio_mask(gpio);
        /* spin_lock(&lock); */
        g->pd  &= ~mask;
        g->pu  &= ~mask;
        g->die |= mask;
        g->dir |= mask;
        /* spin_unlock(&lock); */
    }

    return 0;
}
__attribute__((always_inline_when_const_args))
int gpio_set_uart0(u32 ch)
{
    if (ch > 3) {
        JL_IOMAP->CON3 &= ~BIT(3); //UT0IOEN
    } else {
        JL_IOMAP->CON3 |= BIT(3); //UT0IOEN
        JL_IOMAP->CON3 &= ~BIT(2); //UT0IOEN
        JL_IOMAP->CON0 &= ~(3 << 6);
        JL_IOMAP->CON0 |= ch << 6;
        switch (ch) {
        case 0 :
            gpio_write(IO_PORTA_05, 1);
            gpio_set_direction(IO_PORTA_05, 0);
            gpio_set_die(IO_PORTA_05, 1);
            gpio_set_direction(IO_PORTA_06, 1);
            gpio_set_pull_up(IO_PORTA_06, 1);
            gpio_set_die(IO_PORTA_06, 1);
            break;
        case 1:
            gpio_write(IO_PORTB_04, 1);
            gpio_set_direction(IO_PORTB_04, 0);
            gpio_set_die(IO_PORTB_04, 1);
            gpio_set_direction(IO_PORTB_06, 1);
            gpio_set_pull_up(IO_PORTB_06, 1);
            gpio_set_die(IO_PORTB_06, 1);
            break;
        case 2:
            gpio_write(IO_PORTB_05, 1);
            gpio_set_hd(IO_PORTB_05, 1);
            gpio_set_pull_up(IO_PORTB_05, 1);
            gpio_set_pull_down(IO_PORTB_05, 0);
            gpio_set_direction(IO_PORTB_05, 0);
            gpio_set_die(IO_PORTB_05, 1);
            break;
        case 3 :
            gpio_write(IO_PORTA_11, 1);
            gpio_set_direction(IO_PORTA_11, 0);
            gpio_set_die(IO_PORTA_11, 1);
            gpio_set_direction(IO_PORTA_12, 1);
            gpio_set_pull_up(IO_PORTA_12, 1);
            gpio_set_die(IO_PORTA_12, 1);
            break;
        default:
            return -EINVAL;
        }
    }
    return 0;
}

__attribute__((always_inline_when_const_args))
int gpio_set_uart1(u32 ch)
{
    if (ch > 3) {
        JL_IOMAP->CON3 &= ~BIT(7); //UT1IOEN
    } else {
        JL_IOMAP->CON3 |= BIT(7); //UT1IOEN
        JL_IOMAP->CON3 &= ~BIT(6); //UT1IOEN
        JL_IOMAP->CON1 &= ~(3 << 2);
        JL_IOMAP->CON1 |= ch << 2;
        switch (ch) {
        case 0 :
            gpio_write(IO_PORTB_00, 1);
            gpio_set_direction(IO_PORTB_00, 0);
            gpio_set_die(IO_PORTB_00, 1);
            gpio_set_direction(IO_PORTB_01, 1);
            gpio_set_pull_up(IO_PORTB_01, 1);
            gpio_set_die(IO_PORTB_01, 1);
            break;
        case 1 :
            gpio_write(IO_PORTC_00, 1);
            gpio_set_direction(IO_PORTC_00, 0);
            gpio_set_die(IO_PORTC_00, 1);
            gpio_set_direction(IO_PORTC_01, 1);
            gpio_set_pull_up(IO_PORTC_01, 1);
            gpio_set_die(IO_PORTC_01, 1);
            break;
        case 2:
            gpio_write(IO_PORTA_00, 1);
            gpio_set_direction(IO_PORTA_00, 0);
            gpio_set_die(IO_PORTA_00, 1);
            gpio_set_direction(IO_PORTA_01, 1);
            gpio_set_pull_up(IO_PORTA_01, 1);
            gpio_set_die(IO_PORTA_01, 1);
            break;
        case 3:
            usb_iomode(1);
            //tx dp
            usb_output(IO_PORT_DP - IO_MAX_NUM, 1);
            usb_set_die(IO_PORT_DP - IO_MAX_NUM, 1);
            usb_set_direction(IO_PORT_DP - IO_MAX_NUM, 0);
            usb_set_pull_up(IO_PORT_DP - IO_MAX_NUM, 0);
            usb_set_pull_down(IO_PORT_DP - IO_MAX_NUM, 0);

            //rx dm
            usb_set_die(IO_PORT_DM - IO_MAX_NUM, 1);
            usb_set_direction(IO_PORT_DM - IO_MAX_NUM, 1);
            usb_set_pull_up(IO_PORT_DM - IO_MAX_NUM, 1);
            usb_set_pull_down(IO_PORT_DM - IO_MAX_NUM, 0);

            break;
        default:
            return -EINVAL;
        }
    }
    return 0;
}
__attribute__((always_inline_when_const_args))
int gpio_set_uart2(u32 ch)
{
    if (ch > 3) {
        JL_IOMAP->CON3 &= ~BIT(11); //UT2IOEN
    } else {
        JL_IOMAP->CON3 |= BIT(11); //UT2IOEN
        JL_IOMAP->CON3 &= ~BIT(10); //UT2IOEN
        JL_IOMAP->CON1 &= ~(3 << 14);
        JL_IOMAP->CON1 |= ch << 14;
        switch (ch) {
        case 0 :
            gpio_write(IO_PORTA_02, 1);
            gpio_set_direction(IO_PORTA_02, 0);
            gpio_set_die(IO_PORTA_02, 1);
            gpio_set_direction(IO_PORTA_03, 1);
            gpio_set_pull_up(IO_PORTA_03, 1);
            gpio_set_die(IO_PORTA_03, 1);
            break;
        case 1 :
            gpio_write(IO_PORTA_09, 1);
            gpio_set_direction(IO_PORTA_09, 0);
            gpio_set_die(IO_PORTA_09, 1);
            gpio_set_direction(IO_PORTA_10, 1);
            gpio_set_pull_up(IO_PORTA_10, 1);
            gpio_set_die(IO_PORTA_10, 1);
            break;
        case 2 :
            gpio_write(IO_PORTB_09, 1);
            gpio_set_direction(IO_PORTB_09, 0);
            gpio_set_die(IO_PORTB_09, 1);
            gpio_set_direction(IO_PORTB_10, 1);
            gpio_set_pull_up(IO_PORTB_10, 1);
            gpio_set_die(IO_PORTB_10, 1);
            break;
        case 3 :
            gpio_write(IO_PORTC_04, 1);
            gpio_set_direction(IO_PORTC_04, 0);
            gpio_set_die(IO_PORTC_04, 1);
            gpio_set_direction(IO_PORTC_05, 1);
            gpio_set_pull_up(IO_PORTC_05, 1);
            gpio_set_die(IO_PORTC_05, 1);
            break;
        default:
            return -EINVAL;

        }
    }
    return 0;
}

__attribute__((always_inline_when_const_args))
int gpio_timer_cap_input_channel(u32 gpio, u32 timer_idx)
{
    if (gpio > IO_PORT_DM) {
        return -EINVAL;
    }
    if (gpio == IO_PORT_DP) {
        usb_iomode(1);
        gpio = 61;
    } else if (gpio == IO_PORT_DM) {
        usb_iomode(1);
        gpio = 62;
    }
    JL_IOMAP->CON1 |=  BIT(28 + timer_idx);
    JL_IOMAP->CON2 &= ~(0x3f << 16);
    JL_IOMAP->CON2 |= (gpio << 16);
    return 0;
}

__attribute__((always_inline_when_const_args))
int gpio_irflt_input_channel(u32 gpio, u32 ir_tmr_idx)
{
    if (gpio > IO_PORT_DM) {
        return -EINVAL;
    }
    if (gpio == IO_PORT_DP) {
        usb_iomode(1);
        gpio = 61;
    } else if (gpio == IO_PORT_DM) {
        usb_iomode(1);
        gpio = 62;
    }
    JL_IOMAP->CON0 |=  BIT(10);
    JL_IOMAP->CON0 &= ~(0b11 << 8);
    JL_IOMAP->CON0 |= ((ir_tmr_idx & 0b11) << 8);
    JL_IOMAP->CON2 &= ~(0x3f << 8);
    JL_IOMAP->CON2 |= (gpio << 8);
    return 0;
}

